package me.bbvip.chat.webSocket;

import me.bbvip.chat.entry.MessageRequest;
import me.bbvip.chat.entry.MessageResponse;
import me.bbvip.chat.utils.DateUtils;
import me.bbvip.chat.utils.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.*;

/**
 * websocket消息处理器类
 *
 * @author peiyu
 */
public class EchoHandler extends TextWebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(EchoHandler.class);

    //登录者信息缓存，主要用于把session id和昵称关联起来，用于发送消息
    private WebSocketCache cache = WebSocketCache.me();

    //记录每个session id发送过快的时间，防止用户恶意刷屏
    private Map<String, Date> timeLimitMap = new Hashtable<>();

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage textMessageObj) {
        String msgStr = textMessageObj.getPayload().trim();
        MessageResponse messageResponse = this.getValidaError(msgStr, session);
        if (messageResponse != null) {
            this.sendMessage(session, messageResponse);
            return;
        }
        log.debug("收到消息:{}",msgStr);
        MessageRequest request = JSONUtil.toBean(msgStr, MessageRequest.class);
        //可以用spring 配置一个map<cmd,inteface impl class>来处理.
        switch (request.getCmd()) {
            case "login": {
                //判断是否存在session
                WebSocketSession oldSession = this.getSessionById(session.getId());
                if(oldSession!=null){
                    log.warn("重复登陆!sessionId:{},username:{}",session.getId(),this.getUserCacheName(session.getId()));
                    MessageResponse response = new MessageResponse(
                            MessageResponse.LEVEL_WARN,
                            MessageResponse.MSG_OCDE_SYS,
                            "重复登陆!",
                            "","系统提示",
                            session.getId(),this.getUserCacheName(session.getId())
                    );
                    this.sendMessage(session,response);
                    return;
                }
                log.debug("登陆聊天室:username:{},sessionId:{}",request.getMsg(),session.getId());
                if(this.login(session, request)){
                    //回复sessionId
                    MessageResponse loginResponse = new MessageResponse(
                            MessageResponse.LEVEL_INFO,
                            MessageResponse.MSG_OCDE_RESP_LOGIN,
                            session.getId(),
                            "系统提示"
                    );
                    this.sendMessage(session,loginResponse);
                    MessageResponse response = new MessageResponse(
                            MessageResponse.LEVEL_INFO,
                            MessageResponse.MSG_OCDE_TIP_LOGIN,
                            String.format("用户<%s>上线了.", request.getMsg()),
                            "系统提示"
                    );
                    this.broadcastMessage(response);
                }
                return;
            }
            case "msg":{
                if(StringUtils.isBlank(request.getDirect())){
                    //广场消息
                    MessageResponse response = new MessageResponse(
                            MessageResponse.LEVEL_NORMAL,
                            MessageResponse.MSG_OCDE_MSG,
                            request.getMsg(),
                            session.getId(),
                            this.getUserCacheName(session.getId())
                    );
                    this.broadcastMessage(response);
                }else{
                    //私密消息
                    WebSocketSession directSession = this.getSessionById(request.getDirect());
                    if(directSession==null || !directSession.isOpen()){
                        //session不存在或已经关闭
                        MessageResponse response = new MessageResponse(
                                MessageResponse.LEVEL_WARN,
                                MessageResponse.MSG_OCDE_RESP_MSG,
                                String.format("'%s'发送失败,用户已经下线.",request.getMsg()),
                                "",
                                "系统提示",
                                session.getId(),
                                this.getUserCacheName(session.getId())
                        );
                        this.sendMessage(session,response);
                    }else{
                        MessageResponse response = new MessageResponse(
                                MessageResponse.LEVEL_NORMAL,
                                MessageResponse.MSG_OCDE_SECRET_MSG,
                                request.getMsg(),
                                session.getId(),
                                this.getUserCacheName(session.getId())+"对你说",
                                directSession.getId(),
                                this.getUserCacheName(directSession.getId())
                        );
                        this.sendMessage(directSession,response);
                    }
                }
                return;
            }
            default:{
                log.warn("未知的请求命令.messageRequest:{}",request);
                return;
            }

        }

    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.debug("建立链接");
        //在这里发现了一个从session中获取额外参数的方法，但是在前端还没找到方法可以将数据放进这个里面，如果可以将昵称放进去，那么就不用那么蛋疼的记录登录者的昵称了
        Map<String, Object> attributes = session.getAttributes();
        if (null == attributes || attributes.isEmpty()) {
            log.debug("没有参数....");
        } else {
            attributes.forEach((key, value) -> log.debug("key:{}, value:{}", key, value));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.debug("关闭链接");
        String name = this.getUserCacheName(session.getId());
        this.cache.deleteCache(session.getId());
        this.timeLimitMap.remove(session.getId());
        if (null != name && !"".equals(name)) {
            MessageResponse response = new MessageResponse(
                    MessageResponse.LEVEL_INFO,
                    MessageResponse.MSG_OCDE_SYS,
                    String.format("用户<%s>下线.",name),
                    "系统提示"
            );
            this.broadcastMessage(response,session.getId());
        }
    }


    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        log.debug("handleBinaryMessage:{}", message.toString());
    }

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        log.debug("handlePongMessage:{}", message.toString());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("handleTransportError:", exception);
        String name = this.cache.getUserName(session.getId());
        this.cache.deleteCache(session.getId());
        this.timeLimitMap.remove(session.getId());
        if (session.isOpen()) {
            session.close();
        }
        if (null != name && !"".equals(name)) {
//            if (session.isOpen()) {
//                session.sendMessage(new TextMessage(name + "下线啦!"));
//            }
            this.cache.getAll().forEach(s -> {
                try {
                    String now = DateUtils.date2String(new Date());
                    s.sendMessage(new TextMessage(now + " " + name + "下线啦!"));
                } catch (IOException e) {
                    log.error("异常", e);
                }
            });
        }
    }

    private MessageResponse getValidaError(String msg, WebSocketSession session) {
        if (msg.length() > 200) {
            return new MessageResponse(MessageResponse.LEVEL_WARN,
                    MessageResponse.MSG_OCDE_SYS,
                    "消息太长，刷屏是不对的!",
                    "","系统提示",
                    session.getId(),this.getUserCacheName(session.getId())
                    );
        }
        Date lastTime = this.timeLimitMap.get(session.getId());
        if(lastTime==null){
            return null;
        }
        Date timeLimit = DateUtils.addSeconds(new Date(), -2);
        boolean before = timeLimit.before(lastTime);
        if (before) {
            return new MessageResponse(MessageResponse.LEVEL_WARN,
                    MessageResponse.MSG_OCDE_SYS,
                    "说的太频繁了！刷屏是不对的！",
                    "","系统提示",
                    session.getId(),this.getUserCacheName(session.getId())
            );
        }
        this.timeLimitMap.put(session.getId(),new Date());
        return null;
    }

    private WebSocketSession getSessionById(String sessionId) {
        for (WebSocketSession webSocketSession : this.cache.getAll()) {
            if (webSocketSession.getId().equals(sessionId)) {
                return webSocketSession;
            }
        }
        return null;
    }

    private void sendMessage(WebSocketSession session, MessageResponse messageResponse) {
        try {
            if (session.isOpen())
                session.sendMessage(new TextMessage(JSONUtil.toJson(messageResponse)));
        } catch (IOException e) {
            log.error(String.format("发送消息失败.\r\n sessionId:%s \r\nmessageResponse:%s", session.getId(), messageResponse.toString()), e);
        }
    }

    /**
     * 广播消息
     *
     * @param messageResponse
     */
    private void broadcastMessage(MessageResponse messageResponse) {
        log.debug("发布广播消息:{}",messageResponse.toString());
        this.cache.getAll().forEach(session -> {
            sendMessage(session, messageResponse);
        });
    }

    /**
     * 广播消息
     *
     * @param messageResponse
     * @param filterSessionId 过滤session
     */
    private void broadcastMessage(MessageResponse messageResponse, Set<String> filterSessionId) {
        log.debug("发布广播消息:{}",messageResponse.toString());
        this.cache.getAll().forEach(session -> {
            if (!filterSessionId.contains(session.getId()))
                sendMessage(session, messageResponse);
        });
    }

    /**
     * 广播消息
     *
     * @param messageResponse
     * @param filterSessionId 过滤session
     */
    private void broadcastMessage(MessageResponse messageResponse, String filterSessionId) {
        log.debug("发布广播消息:{}",messageResponse.toString());
        this.cache.getAll().forEach(session -> {
            if (!filterSessionId.equals(session.getId()))
                sendMessage(session, messageResponse);
        });
    }

    private boolean login(WebSocketSession session, MessageRequest messageRequest) {
        String username = messageRequest.getMsg();
        if (username.length() > 10) {
            log.warn("用户名太长，登陆失败.username:{}", username);
            try {
                session.close(CloseStatus.BAD_DATA);
            } catch (IOException e) {
                log.error("关闭session失败.", e);
            }
            return false;
        } else {
            log.debug("{}上线!`sessionid:{}", username, session.getId());
            this.cache.addCache(username, session);
            this.timeLimitMap.put(session.getId(), new Date(0));
            return true;
        }
    }

    private String getUserCacheName(String sessionId){
        return this.cache.getUserName(sessionId);
    }
}
